<script>
import { mapGetters } from 'vuex';
import debounce from 'lodash/debounce';
import { _EDIT, _VIEW } from '@shell/config/query-params';
import { removeAt, findBy } from '@shell/utils/array';
import { clone } from '@shell/utils/object';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { HCI as HCI_LABELS_ANNOTATIONS } from '@shell/config/labels-annotations';
import { isHarvesterSatisfiesVersion } from '@shell/utils/cluster';
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
import { CAPI, SERVICE } from '@shell/config/types';

export default {
  emits: ['update:value'],

  components: {
    LabeledInput,
    LabeledSelect,
  },

  props: {
    value: {
      type:    Array,
      default: null,
    },

    mode: {
      type:    String,
      default: _EDIT,
    },

    // array of services auto-created previously (only relevent when mode !== create)
    services: {
      type:    Array,
      default: () => []
    },

    // workload name
    name: {
      type:    String,
      default: ''
    }
  },

  data() {
    return {
      rows:                [],
      showHostPorts:       false,
      workloadPortOptions: ['TCP', 'UDP']
    };
  },

  computed: {
    ...mapGetters(['currentCluster']),

    canNotAccessService() {
      return !this.$store.getters['cluster/schemaFor'](SERVICE);
    },

    serviceTypeTooltip() {
      return this.canNotAccessService ? this.t('workload.container.noServiceAccess') : undefined;
    },

    isView() {
      return this.mode === _VIEW;
    },

    showAdd() {
      return !this.isView;
    },

    showRemove() {
      return !this.isView;
    },

    serviceTypes() {
      return [
        {
          label: this.t('workload.container.ports.noCreateService'),
          value: ''
        },
        {
          label: this.t('serviceTypes.clusterip'),
          value: 'ClusterIP'
        },
        {
          label: this.t('serviceTypes.nodeport'),
          value: 'NodePort'
        },
        {
          label: this.t('serviceTypes.loadbalancer'),
          value: 'LoadBalancer'
        },
      ];
    },

    clusterIPServicePorts() {
      return ((this.services.filter((svc) => svc.spec.type === 'ClusterIP') || [])[0] || {})?.spec?.ports;
    },

    loadBalancerServicePorts() {
      return ((this.services.filter((svc) => svc.spec.type === 'LoadBalancer') || [])[0] || {})?.spec?.ports;
    },

    nodePortServicePorts() {
      return ((this.services.filter((svc) => svc.spec.type === 'NodePort') || [])[0] || {})?.spec?.ports;
    },

    ipamOptions() {
      return [{
        label: 'DHCP',
        value: 'dhcp',
      }, {
        label: 'Pool',
        value: 'pool',
      }];
    },

    ipamIndex() {
      return this.rows.findIndex((row) => row._serviceType === 'LoadBalancer' && row.protocol === 'TCP');
    },

    serviceWithIpam() {
      return this.services.find((s) => s?.metadata?.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_IPAM]);
    },

    showIpam() {
      let cloudProvider;
      const version = this.provisioningCluster?.kubernetesVersion;

      if (this.provisioningCluster?.isRke2) {
        const machineSelectorConfig = this.provisioningCluster?.spec?.rkeConfig?.machineSelectorConfig || {};
        const agentConfig = (machineSelectorConfig[0] || {}).config;

        cloudProvider = agentConfig?.['cloud-provider-name'];
      } else if (this.provisioningCluster?.isRke1) {
        const currentCluster = this.$store.getters['currentCluster'];

        cloudProvider = currentCluster?.spec?.rancherKubernetesEngineConfig?.cloudProvider?.name;
      }

      return cloudProvider === HARVESTER &&
              isHarvesterSatisfiesVersion(version);
    },

    provisioningCluster() {
      const out = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER).find((c) => c?.status?.clusterName === this.currentCluster.metadata.name);

      return out;
    },
  },

  created() {
    const rows = clone(this.value || []).map((row) => {
      row._showHost = false;
      row._serviceType = row._serviceType || '';
      row._name = row.name ? `${ row.name }` : `${ row.containerPort }${ row.protocol.toLowerCase() }${ row.hostPort || row._listeningPort || '' }`;
      if (row.hostPort || row.hostIP) {
        row._showHost = true;
      }

      row._ipam = '';

      return row;
    });

    this.rows = rows;
    // show host port column if existing port data has any host ports defined
    this.showHostPorts = !!rows.some((row) => !!row.hostPort);
    this.queueUpdate = debounce(this.update, 500);
    this.rows.map((row) => {
      this.setServiceType(row);
      this.setIpam(row);
    });
  },

  methods: {
    add() {
      this.rows.push({
        name:          '',
        expose:        true,
        protocol:      'TCP',
        containerPort: null,
        hostPort:      null,
        hostIP:        null,
        _showHost:     false,
        _serviceType:  '',
        _ipam:         'dhcp',
      });

      this.queueUpdate();

      this.$nextTick(() => {
        const inputs = this.$refs.name;

        inputs[inputs.length - 1].focus();
      });
    },

    remove(idx) {
      removeAt(this.rows, idx);
      this.queueUpdate();
    },

    update() {
      if ( this.isView ) {
        return;
      }
      const out = [];

      for ( const row of this.rows ) {
        const value = clone(row);

        delete value._showHost;
        out.push(value);
      }
      this.$emit('update:value', out);
    },

    setServiceType(row) {
      const { _name } = row;

      if (this.loadBalancerServicePorts) {
        const portSpec = findBy(this.loadBalancerServicePorts, 'name', _name);

        if (portSpec) {
          row['_listeningPort'] = portSpec.port;

          row._serviceType = 'LoadBalancer';

          return;
        }
      } if (this.nodePortServicePorts) {
        const portSpec = findBy(this.nodePortServicePorts, 'name', _name);

        if (portSpec) {
          row['_listeningPort'] = portSpec.nodePort;

          row._serviceType = 'NodePort';

          return;
        }
      } if (this.clusterIPServicePorts) {
        if (findBy(this.clusterIPServicePorts, 'name', _name)) {
          row._serviceType = 'ClusterIP';

          return;
        }
      }

      return '';
    },

    setIpam(row) {
      if (this.serviceWithIpam && row._serviceType === 'LoadBalancer' && row.protocol === 'TCP') {
        row._ipam = this.serviceWithIpam?.metadata?.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_IPAM];
      }
    },
  },
};
</script>

<template>
  <div :style="{'width':'100%'}">
    <p
      v-if="rows.length > 0"
      class="padded"
    >
      {{ t('workload.container.ports.detailedDescription') }}
    </p>
    <div
      v-for="(row, idx) in rows"
      :key="idx"
      class="ports-row"
      :class="{
        'show-host':row._showHost,
        'loadBalancer': row._serviceType === 'LoadBalancer',
        'tcp': row.protocol === 'TCP',
        'show-ipam': showIpam,
      }"
    >
      <div class="service-type">
        <LabeledSelect
          v-model:value="row._serviceType"
          :mode="mode"
          :label="t('workload.container.ports.createService')"
          :options="serviceTypes"
          :disabled="canNotAccessService"
          :tooltip="serviceTypeTooltip"
          @update:value="queueUpdate"
        />
      </div>

      <div class="portName">
        <LabeledInput
          ref="name"
          v-model:value="row.name"
          :mode="mode"
          :label="t('workload.container.ports.name')"
          @update:value="queueUpdate"
        />
      </div>

      <div class="port">
        <LabeledInput
          v-model:value.number="row.containerPort"
          :mode="mode"
          type="number"
          min="1"
          max="65535"
          placeholder="e.g. 8080"
          :label="t('workload.container.ports.containerPort')"
          :required="row._serviceType === 'LoadBalancer' "
          @update:value="queueUpdate"
        />
      </div>

      <div class="protocol col">
        <LabeledSelect
          v-model:value="row.protocol"
          :mode="mode"
          :options="workloadPortOptions"
          :multiple="false"
          :label="t('workload.container.ports.protocol')"
          @update:value="queueUpdate"
        />
      </div>

      <div
        v-if="row._showHost"
        class="targetPort"
      >
        <LabeledInput
          ref="port"
          v-model:value.number="row.hostPort"
          :mode="mode"
          type="number"
          min="1"
          max="65535"
          placeholder="e.g. 80"
          :label="t('workload.container.ports.hostPort')"
          @update:value="queueUpdate"
        />
      </div>

      <div
        v-if="row._showHost"
        class="hostip"
      >
        <LabeledInput
          ref="port"
          v-model:value="row.hostIP"
          :mode="mode"
          placeholder="e.g. 1.1.1.1"
          :label="t('workload.container.ports.hostIP')"
          @update:value="queueUpdate"
        />
      </div>

      <div
        v-if="!row._showHost && row._serviceType !== 'LoadBalancer' && row._serviceType !== 'NodePort'"
        class="add-host"
      >
        <button
          :disabled="mode==='view'"
          type="button"
          class="btn btn-sm role-tertiary"
          @click="row._showHost = true"
        >
          {{ t('workloadPorts.addHost') }}
        </button>
      </div>

      <div v-if="row._serviceType === 'LoadBalancer' || row._serviceType === 'NodePort'">
        <LabeledInput
          ref="port"
          v-model:value.number="row._listeningPort"
          type="number"
          :mode="mode"
          :label="t('workload.container.ports.listeningPort')"
          :required="row._serviceType === 'LoadBalancer' "
          @update:value="queueUpdate"
        />
      </div>

      <div v-if="showIpam && row._serviceType === 'LoadBalancer' && row.protocol === 'TCP'">
        <div v-if="idx === ipamIndex">
          <LabeledSelect
            v-model:value="row._ipam"
            :mode="mode"
            :options="ipamOptions"
            :label="t('servicesPage.harvester.ipam.label')"
            :disabled="mode === 'edit'"
            @update:value="queueUpdate"
          />
        </div>
        <div v-else>
          <LabeledSelect
            v-model:value="rows[ipamIndex]._ipam"
            :mode="mode"
            :options="ipamOptions"
            :label="t('servicesPage.harvester.ipam.label')"
            :disabled="true"
            @update:value="queueUpdate"
          />
        </div>
      </div>

      <div
        v-if="showRemove"
        class="remove"
      >
        <button
          type="button"
          class="btn role-link"
          @click="remove(idx)"
        >
          {{ t('workloadPorts.remove') }}
        </button>
      </div>
    </div>
    <div
      v-if="showAdd"
      class="footer"
    >
      <button
        type="button"
        class="btn role-tertiary add"
        @click="add()"
      >
        {{ t('workloadPorts.addPort') }}
      </button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
$remove: 75;
$checkbox: 75;

.title {
  margin-bottom: 10px;

  .read-from-file {
    float: right;
  }
}
.ports-headers, .ports-row{
  display: grid;
  grid-template-columns: 28% 28% 15% 10% 75px 0.5fr;
  grid-column-gap: $column-gutter;
  margin-bottom: 10px;
  align-items: center;
  & .port{
    display: flex;
    justify-content: space-between;
  }

  &.show-host{
    grid-template-columns: 20% 20% 145px 90px 140px .5fr .5fr;
  }

  &.show-ipam.loadBalancer.tcp{
    grid-template-columns: 20% 20% 145px 90px .5fr 140px .5fr;
  }

  &.show-ipam.show-host.loadBalancer{
    grid-template-columns: 20% 10% 135px 90px 105px .5fr .5fr .5fr;
  }

  &.show-ipam.show-host.loadBalancer.tcp{
    grid-template-columns: 12% 10% 135px 90px 105px .5fr .5fr 100px .5fr;
  }
}

.add-host {
  justify-self: center;
}

.protocol {
  height: 100%;
}

.ports-headers {
  color: var(--input-label);
}

.toggle-host-ports {
  color: var(--primary);
}

.remove BUTTON {
  padding: 0px;
}

.ports-row INPUT {
  height: 50px;
}

.footer {
  .protip {
    float: right;
    padding: 5px 0;
  }
}
.ports-row .protocol :deep() .unlabeled-select,
.ports-row .protocol :deep() .unlabeled-select .v-select {
  height: 100%;
}
.ports-row .protocol :deep() .unlabeled-select .vs__dropdown-toggle {
  padding-top: 12px;
}
</style>
